package com.dahuyou.trigger.http;

import com.dahuyou.types.util.JwtUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * 模拟授权/认证服务器，基于OIDC授权认证协议
 */
@Controller
@RequestMapping("/oidcProvider")
public class OIDCProviderController {
    /*
    sub -> id token
     */
    private Map<String, String> subAndIdTokenMap = new HashMap<>();

    /**
     * 判断用户是否登录
     *
     * @param request
     * @return
     */
    // http://localhost:8899/oidcProvider/auth/client_id=app1&redirect_uri=' + encodedURI + '&scope=openid profile&response_type=code&sub=xxxx
    @RequestMapping("/auth")
//    public String auth(HttpServletRequest request, HttpServletResponse response) {
    public void auth(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String username = request.getParameter("sub");
        String redirectUri = request.getParameter("redirect_uri");
        String appId = request.getParameter("app_id");
        request.setAttribute("redirect_uri", redirectUri);
        // TODO 各种验证
        System.out.println("com.dahuyou.trigger.http.OIDCProviderController.auth TODO 各种验证,验证redirect uri，appid，app_secret等");
        // 未登录，重定向到登录页面去登陆
        if (username == null) {
            response.sendRedirect("http://localhost:8899/oidcProvider/login?redirect_uri=" + redirectUri);
            return;
        }
        // 如果没有idtoken，也重新登陆
        if (!usernameAndIdTokenMap.containsKey(username)) {
            response.sendRedirect("http://localhost:8899/oidcProvider/login?redirect_uri=" + redirectUri);
            return;
        }
        // 否则就直接生成code，并回调code
        System.out.println("com.dahuyou.trigger.http.OIDCProviderController.doLogin 验证用户：" + username + "通过，生成code值，并回调");
        // 授权码
        String code = generateCode(appId, username);//模拟登陆用户为USERTEST
//        codeScopeMap.put(code, rscope);//授权范围与授权码做绑定

//                Map<String, String> params = new HashMap<String, String>();
//                params.put("code", code);

//                String toAppUrl = URLParamsUtil.appendParams(redirectUri, params);//构造第三方软件的回调地址，并重定向到该地址
        codeAndUsernameMap.put(code, username);
        String toAppUrl = redirectUri + "?code=" + code;
        response.sendRedirect(toAppUrl);//授权码流程的【第二次】重定向

//        return "test";
    }

    @RequestMapping("/login")
    public String login(HttpServletRequest request, HttpServletResponse response) {
        String redirectUri = request.getParameter("redirect_uri");
        request.setAttribute("redirect_uri", redirectUri);
        return "login";
    }

    // 记录code对应的用户
    Map<String, String> codeAndUsernameMap = new HashMap<>();

    @RequestMapping("/doLogin")
//    public String doLogin(HttpServletRequest request, HttpServletResponse response) throws Exception {
    public void doLogin(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("com.dahuyou.trigger.http.OIDCProviderController.doLogin TODO 各种验证,验证redirect uri，appid，app_secret等");
        String appId = request.getParameter("app_id");
        //  <input type="hidden" name="redirect_url" value="<%=request.getAttribute("redirect_uri")%>"><br><br>
        String redirectUri = request.getParameter("redirect_uri");
        //  用户名：<input type="text" name="username" value="张三的小姐姐"><br><br>
        String username = request.getParameter("username");
        //  密码：<input type="password" name="pwd" value="11111"><br><br>
        String password = request.getParameter("pwd");
        // 这里用户名密码无脑通过，正常需要查询库来做验证
        System.out.println("com.dahuyou.trigger.http.OIDCProviderController.doLogin 验证用户：" + username + ",密码：" + password + "通过，生成code值，并回调");
        // 授权码
        String code = generateCode(appId, username);//模拟登陆用户为USERTEST
//        codeScopeMap.put(code, rscope);//授权范围与授权码做绑定

//                Map<String, String> params = new HashMap<String, String>();
//                params.put("code", code);

//                String toAppUrl = URLParamsUtil.appendParams(redirectUri, params);//构造第三方软件的回调地址，并重定向到该地址
        // 这里直接写死查询到的用户id
        String userId = "5623985";
//        codeAndUsernameMap.put(code, username);
        codeAndUsernameMap.put(code, userId);
        String toAppUrl = redirectUri + "?code=" + code;
        response.sendRedirect(toAppUrl);//授权码流程的【第二次】重定向
//        return "login";
    }

    Map<String, String> usernameAndIdTokenMap = new HashMap<>();
    @RequestMapping("/exchangeAccessTokenByCode")
    @ResponseBody
    public void exchangeAccessTokenByCode(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        System.out.println("com.dahuyou.trigger.http.OIDCProviderController.exchangeAccessTokenByCode TODO 各种验证,验证redirect uri，appid，app_secret等");
        String appId = request.getParameter("app_id");
        String appSecret = request.getParameter("app_secret");
        System.out.println("com.dahuyou.trigger.http.OIDCProviderController.exchangeAccessTokenByCode TODO 各种验证,验证redirect uri，appid，app_secret等");
        String code = request.getParameter("code");

        // 用户登录时输入的用户名
//        String username = codeAndUsernameMap.get(code);
        String userId = codeAndUsernameMap.get(code);
//            String accessToken = generateAccessToken(appId, "USERTEST");//生成访问令牌access_token的值
        // TODO 这里还需要refresh_token，简单起见就忽略了！！！
//        String accessToken = JwtUtils.generateJwt(username);
        String accessToken = JwtUtils.generateJwt(userId);
        // 使用用户登录时输入的用户名作为sub，来生成id_token
        /**
         * iss，令牌的颁发者，其值就是身份认证服务（OP）的 URL。
         * sub，令牌的主题，其值是一个能够代表最终用户（EU）的全局唯一标识符。
         * aud，令牌的目标受众，其值是三方软件（RP）的 app_id。
         * exp，令牌的到期时间戳，所有的 ID 令牌都会有一个过期时间。
         * iat，颁发令牌的时间戳。
         * @param iss
         * @param sub
         * @param aud
         * @param exp
         * @param iat
         * @return
         */
        String iss = "http://localhost:8899/auth";
//        String sub = username;
        String sub = userId;
        String aud = appId;
        Date exp = new Date(System.currentTimeMillis() + 10000);
        Date iat = new Date((System.currentTimeMillis()));
        String idToken = JwtUtils.generateIdTokenJwt(iss, sub, aud, exp, iat);
//        usernameAndIdTokenMap.put(username, idToken);
        usernameAndIdTokenMap.put(userId, idToken);

//            tokenScopeMap.put(accessToken, codeScopeMap.get(code));//授权范围与访问令牌绑定

//            String refreshToken = generateRefreshToken(appId, "USERTEST");//生成刷新令牌refresh_token的值
        // TODO: 2020/2/28 将accessToken和refreshToken做绑定 ，将refreshToken和codeScopeMap做绑定
        response.getWriter().write(accessToken + "|" + idToken);
    }

    /*
    UserInfo 端点
    获取 UserInfo 信息
     */
    @RequestMapping("/queryUserInfo")
    @ResponseBody
    public void queryUserInfo(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        System.out.println("com.dahuyou.trigger.http.OIDCProviderController.exchangeAccessTokenByCod 各种验证,验证access_token，app_secret等");
        String accessToken = request.getParameter("access_token");
        String sub = request.getParameter("sub");

        response.setContentType("text/html;charset=utf-8");

        // 简单起见，这里直接返回一个结果了
        response.getWriter().write(sub + "_30岁_未婚_热恋中！！！");
    }

    // 存储临时的授权码信息，正常该值存储在如redis中的nosql，并设置5~10分钟的有效期（文档规定的是10分钟）
    private static Map<String, String> codeMap = new HashMap<String, String>();

    /**
     * 生成code值
     *
     * @return
     */
    private String generateCode(String appId, String user) {
        Random r = new Random();
        StringBuilder strb = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            strb.append(r.nextInt(10));
        }

        String code = strb.toString();
        codeMap.put(code, appId + "|" + user + "|" + System.currentTimeMillis());//在这一篇章我们仅作为演示用，实际这应该是一个全局内存数据库，有效期官方建议是10分钟

        return code;
    }

}